/**
 * \file FFT.cpp
 * \author Florian LANCE - Armand POLI
 * \version 1.0
 * \date octobre 2011
 */

#include "FFT.h"

#include <cassert>
#include <iostream>
#include <math.h>


/// ################################################################## CONSTRUCTEURS / DESTRUCTEURS

FFT::FFT()
{}


FFT::FFT(Image<1> _img, const double _max, const int _diffX , const int _diffY) : max(_max)
{
    x.clear();
    X.clear();
    imageC.clear();

    diffX = _diffX;
    diffY = _diffY;

    largeurImage = _img.getLargeur();
    hauteurImage = _img.getHauteur();


    cx c(0,0);

    for(int j = 0; j < hauteurImage; j++)
    {
         for(int i = 0; i < largeurImage; i++)
        {

            c = cx(_img.getValPixel(i,j,0));
            imageC.push_back(c);
        }
    }
    tailleOriginale = _img.getHauteur()*_img.getLargeur();
}


FFT::~FFT(){}


/// ################################################################## ACCESSEURS

tab_cx FFT::getLigne(const int _indiceL)
{
    tab_cx l;

    for(int i = 0; i < largeurImage; i++)
    {
        l.push_back(imageC[(largeurImage) * _indiceL + i]);
    }

    return l;
}

tab_cx FFT::getColonne(const int _indiceC)
{
    tab_cx c;

    for(int i = 0; i < hauteurImage; i++)
    {
        c.push_back(imageC[(largeurImage) * i + _indiceC]);
    }

    return c;
}

/// ################################################################## MUTATEURS

void FFT::setLigne(const int _indiceL, tab_cx &_l)
{

    for(int i = 0; i < largeurImage; i++)
    {
        imageC[(largeurImage) * _indiceL + i]  = _l[i];
    }
}

void FFT::setColonne(const int _indiceC, tab_cx &_c)
{
    for(int i = 0; i < hauteurImage; i++)
    {
        imageC[(largeurImage) * i + _indiceC]  = _c[i];
    }
}

void FFT::setxX(const tab_cx &_x)
{
    x.clear();
    X.clear();

    for(uint i = 0; i < _x.size(); i++)
    {
        x.push_back(_x[i]);
        X.push_back(_x[i]);
    }

    T = x.size();
    N = T/2;

}

void FFT::setX(const tab_cx &_X)
{
    imageC = _X;
}

/// ################################################################## FONCTIONS


tab_cx FFT::calculTFImage()
{
    tab_cx c;
    tab_cx l;


    for(int i = 0; i < largeurImage; i++)
      {
         c = getColonne(i);
         setxX(c);
         c = calculFourierRapide();
         setColonne(i,c);
     }


    for(int i = 0; i < hauteurImage; i++)
        {
            l = getLigne(i);
            setxX(l);
            l = calculFourierRapide();
            setLigne(i,l);
        }

    return imageC;
}

tab_cx FFT::calculInverseTFImage()
{
     tab_cx l;
     tab_cx c;

    for(int i = 0; i < hauteurImage; i++)
        {
            l = getLigne(i);
            setxX(l);
            l = calculInverseFourierRapide();
            setLigne(i,l);
        }

    for(int i = 0; i < largeurImage; i++)
      {
         c = getColonne(i);
         setxX(c);
         c = calculInverseFourierRapide();
         setColonne(i,c);
     }


    return imageC;
}


void FFT::aff(tab_cx &_tabC)
{
    cout << endl << "Affichage tabC de taille " << _tabC.size() << " s" << endl;
    for(uint i = 0; i < _tabC.size(); i++)
    {
        cout << _tabC[i].real() << " " << _tabC[i].imag() <<"_i | ";
    }
}


void FFT::indicerFourierRapide()
{
    tab_cx temp;
    temp.assign(T,cx(0,0)); // création d'un tableau de complexe temporaire de taille T

    int lActuelle = T/2;
    int indice,type=0;

    int i;
    while(lActuelle!=1)
    {
        i=0;
        indice=0;

        while(i < T)
        {
            for( int j = 0 ; j<lActuelle ; j++ )
            {
                if(!(type%2))
                {
                    temp[indice] = X[i];
                    temp[indice+lActuelle] = X[i+1];
                }
                else
                {
                    X[indice] = temp[i];
                    X[indice + lActuelle] = temp[i+1];
                }

                i+=2;
                indice++;
            }
            indice = i;
        }

        type++;
        lActuelle/=2;
    }

    uint a;
    if(type%2==1)
        for(a=0;a<temp.size();a++)
            X[a]=temp[a];

}

tab_cx FFT::calculFourierRapide()
{
    indicerFourierRapide();

    uint lActuelle=2;
    int i,indice;

    const cx c_imag(0,1);
    while(lActuelle <= X.size())
    {
        i=0;
        while(i < T)
        {
            indice = 0;
            for(uint j = 0 ; j < lActuelle/2 ; j++ )
            {
                cx c1 = X[i + indice];
                cx c2 = X[i + indice + lActuelle/2];
                X[i + indice]               = c1 + c2 * std::exp(-c_imag * M_PI * (indice*2.0 /(double)(lActuelle)));
                X[i + indice + lActuelle/2] = c1 - c2 * std::exp(-c_imag * M_PI * (indice*2.0 /(double)(lActuelle)));
                indice++;

            }
            i += lActuelle;
        }

        lActuelle*=2;
    }

    for(int i = 0; i < T; i++)
    {
        X[i].real() /= T;
        X[i].imag() /= T;
    }

    return X;
}


tab_cx FFT::calculInverseFourierRapide()
{
    indicerFourierRapide();

    uint lActuelle = 2;
    int i, indice;

    const cx c_imag(0,1);

    while(lActuelle<=X.size())
    {
        i=0;
        while(i < T)
        {
            indice = 0;
            for(uint j = 0 ; j < lActuelle/2 ; j++ )
            {
                cx c1 = X[i + indice];
                cx c2 = X[i + indice + lActuelle/2];
                X[i + indice]               = c1 + c2 * std::exp(c_imag * M_PI * (indice*2.0 /(double)(lActuelle)));
                X[i + indice + lActuelle/2] = c1 - c2 * std::exp(c_imag * M_PI * (indice*2.0 /(double)(lActuelle)));
                indice++;
            }
            i += lActuelle;
        }

        lActuelle *= 2;
    }

    return X;

}


Image<1> FFT::calculSpectreAmplitude(const tab_cx &_t,const bool VISUALISATION_STANDARD)
{
    double res;
    Image<1> retour(largeurImage-diffX,hauteurImage-diffY);

    for(int j = 0; j < hauteurImage - diffY; j++)
    {
        for(int i = 0; i < largeurImage - diffX; i++)
        {
//            if(i <= (largeurImage-diffX)/2 && j <= (hauteurImage-diffY)/2)
                res = sqrt(pow(_t[j*largeurImage + i].real(),2) + pow(_t[j*largeurImage + i].imag(),2));

//            else  if(i > (largeurImage-diffX)/2 && j <= (hauteurImage-diffY)/2)
//                            res = sqrt(pow(_t[(hauteurImage - j)*largeurImage + i].real(),2) + pow(_t[(hauteurImage - j)*largeurImage + i].imag(),2));
//            else if(i <= (largeurImage-diffX)/2 && j > (hauteurImage-diffY)/2)
//                res = sqrt(pow(_t[(hauteurImage - j)*largeurImage + i].real(),2) + pow(_t[(hauteurImage - j)*largeurImage + i].imag(),2));
//            else
//                res = sqrt(pow(_t[(hauteurImage - j)*largeurImage + (hauteurImage-i)].real(),2) + pow(_t[(hauteurImage - j)*largeurImage + (hauteurImage-i)].imag(),2));

            if(res > max ) res = max;
            res *= 255/max;
            if(VISUALISATION_STANDARD)
                retour.setValPixel(i,j,0, res);
            else
                retour.setValPixel((i+(largeurImage- diffX)/2)%(largeurImage- diffX), (j+(hauteurImage- diffY)/2)%(hauteurImage-diffY),0, res);

        }
    }


    return retour;
}

Image<1> FFT::calculSpectrePhase(const tab_cx &_t,const bool VISUALISATION_STANDARD)
{
    double res;
    Image<1> retour(largeurImage-diffX,hauteurImage-diffY);

    for(int i = 0; i < largeurImage - diffX; i++)
    {
        for(int j = 0; j < hauteurImage - diffY; j++)
        {
            res = atan(_t[i*hauteurImage + j].real() / _t[i*hauteurImage + j].imag());
            res += M_PI/2.f;
            res /= M_PI;
            res *= 255;
            if(VISUALISATION_STANDARD)
                retour.setValPixel(i,j,0, res);
            else
                retour.setValPixel((i+(largeurImage- diffX)/2)%(largeurImage- diffX), (j+(hauteurImage- diffY)/2)%(hauteurImage-diffY),0, res);

        }
    }


    return retour;
}


void FFT::calculImage(const tab_cx &_t, Image<1> &_img)
{
    uint res;

    for(int i = 0; i < largeurImage - diffX; i++)
    {
        for(int j = 0; j < hauteurImage - diffY; j++)
        {
            res =_t[j*largeurImage + i].real();
            if(res > 255)res = 255;
            _img.setValPixel(i,j,0, res);
        }
    }
}

Image<1> FFT::calculImage(const tab_cx &_t)
{
    Image<1> retour;
    retour.setLargeur(largeurImage-diffX);
    retour.setHauteur(hauteurImage-diffY);
    retour.setValMaxPGM(255);
    std::vector<Pixel<1> > tab;

    uint res = 0;

     for(int j = 0; j < hauteurImage-diffY; j++)
    {
       for(int i = 0; i < largeurImage-diffX; i++)
            {
                res =_t[j*(largeurImage) + i].real();
                if(res > 255)res = 255;
                tab.push_back(res);
            }

    }
    retour.setTab(tab, retour.getHauteur()*retour.getLargeur() );

    return retour;
}


void FFT::affImage(const tab_cx &_X,  Image<1> &_img)
{
    uint val;

    for(int i = 0; i < largeurImage - diffX; i++)
    {
        for(int j = 0; j < hauteurImage - diffY; j++)
        {
            val = _X[j*largeurImage + i].real();
            cout << val << " ";
            _img.setValPixel(i,j,0, val);
        }
    }
}

